#include "sptlib.h"

#pragma pack (1)

typedef std::map<std::string, std::string> parmMap;

char * merge( int args, ...) {
	va_list ap; /* declare an argument pointer to a variable arg list */
        va_start(ap, args); /* initialize arg pointer using last known arg */
	int len = 0;
	const char * foo = "";
        for (int i  = 0; i < args; i++) {
		foo = va_arg(ap, const char *);
		len += strlen(foo);
        } // for
        va_end(ap); /* restore any special stack manipulations */
	char* bar = new char[len+1];
	bar[0] = '\0';
        va_start(ap, args); /* initialize arg pointer using last known arg */
        for (int i  = 0; i < args; i++) {
			foo = va_arg(ap, const char *);	
			strcat(bar, foo);
        } // for
        va_end(ap); /* restore any special stack manipulations */
	return bar;
};

std::string trim(std::string str, std::string whitespace) {
	int a = str.find_first_not_of(whitespace);
	int b = str.find_last_not_of(whitespace)+1;
	return str.substr(a, b-a);
}
std::string trim(char * str, std::string whitespace) {
	std::string strng(str);
	return trim(strng, whitespace = whitespace);
}

int mInt(std::string val) {
	int ret;
	ret = atoi(val.c_str());
	return ret;
};

parmMap smLoadParms(const char * parmfile, char * delim) {
	parmMap ret;
	std::ifstream pfile (parmfile);
	int posparmcount = 0;
	char forints [ 20 ] ; // for holding a string representation of the number of unnammed arguments
	char * val, * dlm;
	char temp;
	char line[MAXPARMLENGTH];
	while (pfile.getline(line, MAXPARMLENGTH, '\n')) {
		if (line[0] == '#' or strcmp(line, "") == 0) continue;
		dlm = strpbrk(line,delim);
		if (dlm == NULL) {
			// positional parameters mapped to "-0" to "-N"
			sprintf(forints, "-%d", posparmcount);
			ret[trim(forints)]=trim(line);
			posparmcount++;
			continue;
		}
		temp =dlm[0];
		dlm[0]='\0';
		val = dlm+1;
		if (strcmp(val, "")==0) ret[trim(line)]="";
		else ret[trim(line)]=trim(val);
		dlm[0] = temp;
	}
	sprintf(forints, "%d", posparmcount);
	// number of positional parameters found: the "n" in the range "-0" to "-N"
	ret["posparmcount"]=forints;
	return ret;
}
// Just in case a c++ string is passed instead of a C string
parmMap smLoadParms(std::string parmfile, char * delim ) {
	return smLoadParms(parmfile.c_str(), delim = delim) ;
}
// Add config file in lower priority than existing parms
parmMap smLoadParms(const char * parmfile, parmMap parms, char * delim) {
	// Add config file in lower priority than existing parms
	parmMap ret(parms);
	parmMap tmp = smLoadParms(parmfile, delim = delim) ;
	parms.insert(tmp.begin(),tmp.end());
	return parms;
}
// Add config file in lower priority than existing parms, when a c++ string is passed instead of a C string
parmMap smLoadParms(std::string parmfile, parmMap parms, char * delim) {
	return smLoadParms(parmfile.c_str(), parms, delim = delim) ;
}
parmMap smParms(int argc, char *argv[], bool autoload, char * delim) {
	parmMap ret;
	int posparmcount = 0;
	char temp;
	char forints [ 20 ] ; // for holding a string representation of the number of unnammed arguments
	char * key, * val, * dlm;
	for (int i=1;i<argc;i++) {
		if (argv[i][0] == '-') {
			key = argv[i]+1;
			if (key[0] == '-') key++;
			if (argc > i+1 and argv[i+1][0] != '-' and strpbrk(argv[i+1],delim) == NULL) {
				ret[key]=argv[i+1];
				i++; }
			else ret[key]="";
		} else {
			dlm = strpbrk(argv[i],delim);
			if (dlm == NULL) {
				// positional parameters mapped to "-0" to "-N"
				sprintf(forints, "-%d", posparmcount);
				ret[forints]=argv[i];
				posparmcount++;
				continue;
			}
			temp =dlm[0];
			dlm[0]='\0';
			val = dlm+1;
			if (strcmp(val, "")==0) ret[argv[i]]="";
			else ret[argv[i]]=val;
			dlm[0] = temp;
		}
	}
	if (autoload and ret.count("conf") > 0) {
		ret = smLoadParms(ret["conf"].c_str(), ret);
	}
	sprintf(forints, "%d", posparmcount);
	// number of positional parameters found: the "n" in the range "-0" to "-N"
	ret["posparmcount"]=forints;
	return ret;
}

parmMap smParms(int argc, char *argv[], parmMap parms, char * delim) {
	// Add arguments in higher priority than existing parms
	parmMap ret = smParms(argc, argv);
	ret.insert(parms.begin(),parms.end());
	return ret;
}

void asDefault(parmMap &parmmm, std::string key, std::string val) {
	parmmm.insert ( std::pair<std::string,std::string>(key,val) );
}

void parmDump(parmMap parms) {
	parmMap::iterator it;
	for ( it=parms.begin() ; it != parms.end(); it++ )
		std::cout << (*it).first << " = " << (*it).second << std::endl;
}
void parmDump(parmMap parms, const char * filename) {
	parmMap::iterator it;
	std::ofstream ofile(filename);
	for ( it=parms.begin() ; it != parms.end(); it++ )
		ofile << (*it).first << " = " << (*it).second << std::endl;
}

void upcase(std::string &str) {
	std::transform(str.begin(), str.end(), str.begin(),(int(*)(int)) std::toupper);
}
void downcase(std::string &str) {
	std::transform(str.begin(), str.end(), str.begin(),(int(*)(int)) std::tolower);
}

int convertToSASDate (int yyyymmdd) {
	int yr = yyyymmdd / 10000;
	int mnth = (yyyymmdd - (yr * 10000)) / 100;
	int day = yyyymmdd - (yr * 10000) - (mnth * 100);
	int a = (14-mnth)/12;
	int y = yr+4800-a;
	int m = mnth + 12*a - 3;
	return (day + (153*m+2)/5 + y*365 + y/4 -2469031); //- 32083 -2436948);
};

void smDate::init(int i) {
		if (i < 19000000) {
			sasDate = i;
			sasdate_to_date();
			tagDate = year * 10000 + month*100 + day;
		} else {
			tagDate = i;
			year = i/10000;
			i -= year*10000;
			month = i/100;
			i -= month*100;
			day = i;
			date_to_sasdate();
		}
	}
void smDate::strinit(std::string dt) {
		//unsigned int pos, pos2;
		size_t pos, pos2;
		int i;
		pos = dt.find_first_not_of("0123456789",0);
		if (pos == std::string::npos) {
			init(atoi(dt.c_str()));
		}
		else if (dt[pos] == '-' ) {
			i = atoi(dt.substr(0,pos).c_str()) * 10000; // year
			pos2 = dt.find_first_of("-",pos+1);
			if (pos == std::string::npos) {throw std::invalid_argument("Unrecognized date format");};
			i += atoi(dt.substr(pos+1, pos2 - 1).c_str())*100; // month
			i += atoi(dt.substr(pos2 + 1).c_str()); // day
			init(i);
		}
		else if (dt[pos] == '/' ) {
			// This assumes US date order.
			i = atoi(dt.substr(0,pos).c_str()) * 100; // month
			pos2 = dt.find_first_of("/",pos+1);
			if (pos == std::string::npos) {throw std::invalid_argument("Unrecognized date format");};
			i += atoi(dt.substr(pos+1, pos2 - 1).c_str()); // day
			i += atoi(dt.substr(pos2 + 1).c_str()) * 10000; // year
			init(i);
		}	
		else {throw std::invalid_argument("Unrecognized date format");};
	}
smDate::smDate() { init(0); }
smDate::smDate(int i) { init(i);}
smDate::smDate(std::string dt) {strinit(dt);}
smDate::smDate(char * dt) {
		std::string str = dt;
		strinit(str);
	}
	/*
	 * The following two functions are modified from code Copyright (c) 2008 by Claus Tondering
	 *   (claus@tondering.dk) under the Boost Software License  - Version 1.0 - August 17th, 2003
	*/
	/*
	 * date_to_jdn:
	 * Calculates the Julian Day Number for a given date.
	 * Input parameters:
	 *     Calendar style (JULIAN or GREGORIAN)
	 *     Year (must be > -4800). The year 1 BC must be given as 0, the
	 *         year 2 BC must be given as -1, etc.
	 *     Month (1..12)
	 *     Day (1..31)
	 * Returns:
	 *     Julian Day Number
	 *
	 * Reference: Section 2.16.1 of version 2.9 of the FAQ.
	 */
int smDate::date_to_jdn(int year, int month, int day)
	{
	    int a = (14-month)/12;
	    int y = year+4800-a;
	    int m = month + 12*a - 3;
	    return day + (153*m+2)/5 + y*365 + y/4 - 32083;
	}
	/*
	 * jdn_to_date:
	 * Calculates the date for a given Julian Day Number.
	 * Input parameter:
	 *     Calendar style (JULIAN or GREGORIAN)
	 *     Julian Day Number
	 * Output parameters:
	 *     Address of year. The year 1 BC will be stored as 0, the year
	 *         2 BC will be stored as -1, etc.
	 *     Address of month (1..12)
	 *     Address of day (1..31)
	 *
	 * Reference: Section 2.16.1 of version 2.9 of the FAQ.
	 */
void smDate::jdn_to_date(int JD, int *year, int *month, int *day)
	{
	    int b, c, d, e, m;
	
		b = 0;
		c = JD + 32082;
		
	    d = (4*c+3)/1461;
	    e = c - (1461*d)/4;
	    m = (5*e+2)/153;
	
	    *day   = e - (153*m+2)/5 + 1;
	    *month = m + 3 - 12*(m/10);
	    *year  = b*100 + d - 4800 + m/10;
	}
void smDate::date_to_sasdate() {
		sasDate = date_to_jdn(year, month, day)-2436948;
	}
void smDate::sasdate_to_date() {
		jdn_to_date((int)(sasDate + 2436948), &year, &month, &day);
	}

void oBuffer::open(std::string fileName, std::ios_base::openmode mode, long int log2_bufsize) {
      OUTBUFSIZE = (long int) std::pow((float) 2, (float)log2_bufsize);
      buffer = (char *) new char[OUTBUFSIZE];
      outF.open(fileName.c_str(), mode);
      currpos = 0;
      };
void oBuffer::flush() {
      outF.write(buffer, currpos);
      outF.flush();
      currpos = 0;
      };
void oBuffer::write(const char * el, int el_size) {
      if (currpos + el_size >= OUTBUFSIZE) flush();
      memcpy(buffer + currpos, el, el_size);
      currpos += el_size;
      };
void oBuffer::close() {
	      flush();
	      delete[] buffer;
	      outF.close();
      }
oBuffer& oBuffer::operator<< (std::string str){write(str.c_str(), str.size()); return *this;}
oBuffer& oBuffer::operator<< (char* str){write(str, strlen(str)); return *this;}
oBuffer& oBuffer::operator<< (char str){write(&str, sizeof(char)); return *this;}
oBuffer& oBuffer::operator<< (float& num){write((char *)&num, sizeof(float)); return *this;}
oBuffer& oBuffer::operator<< (double& num){write((char *)&num, sizeof(double)); return *this;}
oBuffer& oBuffer::operator<< (long double& num){write((char *)&num, sizeof(long double)); return *this;}
oBuffer& oBuffer::operator<< (short& num){write((char *)&num, sizeof(short)); return *this;}
oBuffer& oBuffer::operator<< (unsigned short& num){write((char *)&num, sizeof(unsigned short)); return *this;}
oBuffer& oBuffer::operator<< (int& num){write((char *)&num, sizeof(int)); return *this;}
oBuffer& oBuffer::operator<< (unsigned int& num){write((char *)&num, sizeof(unsigned int)); return *this;}
oBuffer& oBuffer::operator<< (long& num){write((char *)&num, sizeof(long)); return *this;}
oBuffer& oBuffer::operator<< (unsigned long& num){write((char *)&num, sizeof(unsigned long)); return *this;}
oBuffer& oBuffer::operator<< (long long& num){write((char *)&num, sizeof(long long)); return *this;}
oBuffer& oBuffer::operator<< (unsigned long long& num){write((char *)&num, sizeof(unsigned long long)); return *this;}
oBuffer& oBuffer::operator<< (std::stringstream& strb){std::string str=strb.str(); write(str.c_str(), str.size()); return *this;}

std::string IntToStr(int x) {
  std::stringstream y;
  y << x;
  return y.str();
}

